#include <unordered_map>
#include <string>
#include <functional>
#include <any>
#include <memory>

class IocContainer {
public:
	template<typename Ty, typename Depend, typename ...Args>
	void registerType(std::string key) {
		std::function<Ty* (Args...)> create_fun;	
		if constexpr (std::is_base_of_v<Ty, Depend>) {
			create_fun = [](Args... args) {
				return new Depend(args...);
				};
		}
		else {
			create_fun = [](Args... args) {
				return new Ty(new Depend(args...));
				};
		}

		_register(key, create_fun);
	}

	template<typename Ty, typename ...Args>
	void registerTypeNormal(std::string key) {
		std::function<Ty* (Args...)> create_fun = [=](Args... args) {
			return new Ty(args...);
			};
		_register(key, create_fun);
	}

	template<typename Ty, typename ...Args>
	Ty* resolve(std::string key, Args... args) {
		if (m_containers.count(key) == 0) {
			throw std::invalid_argument("Not found this key : " + key);
		}
		auto create_fun = std::any_cast<std::function<Ty* (Args... )>>(m_containers[key]);
		return create_fun(args...);
	}

	template<typename Ty, typename ...Args>
	std::shared_ptr<Ty> resolveShared(std::string key, Args ...args) {
		return std::shared_ptr<Ty>(resolve<Ty, Args...>(key, args...));
	}

private:
	void _register(std::string& key, std::any create_fun) {
		if (m_containers.count(key) > 0) {
			throw std::invalid_argument("Has this key : " + key);
		}
		m_containers.emplace(key, create_fun);
	}

	std::unordered_map<std::string, std::any> m_containers;
};
